﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class DrawCanvas : MonoBehaviour
{
	enum CanvasType
	{
		InputCanvas,
		CorrectedCanvas,
	}

	// Constants

	const int		c_layer_Canvas	= 9;				// 캔버스 레이어 (현재 표시되는 레이어)
	const int		c_layer_Culling	= 10;				// 컬링 레이어 (표시하지 않는 레이어)


	// Properties

    [SerializeField]
    GLRenderCanvas  m_inputCanvas;                      // 인풋이 있는 동안에만 잠시 그려지는 캔버스
    [SerializeField]
    GLRenderCanvas  m_correctedCanvas;                  // 결과가 저장되는 캔버스

	// Members

	CanvasType				m_curCanvasType;			// 현재 캔버스 종류
    GLRenderCanvas          m_drawCanvas;               // 스위칭되면서 그려지는 캔버스
    bool                    m_isCurve;                  // 그리고 있는 선이 곡선인지 여부

	bool                    m_applyFlag	= false;	    // 입력이 끝난 후 Apply를 한번이라도 했는지 여부
	Camera                  m_mainCam;			    	// 메인 카메라 캐시
    LinkedList<Vector2>     m_inputPointList;           // 입력한 점이 저장되는 리스트

    float                   m_screenSize;

	public bool isCurve
	{
		get	{ return m_isCurve; }
		set { m_isCurve	= value; }
	}

	void Start ()
	{
        m_inputPointList               = new LinkedList<Vector2>();
        m_inputCanvas.Clear(Color.white);
        m_correctedCanvas.Clear(Color.white);

        m_drawCanvas	= m_correctedCanvas; 			// 캔버스 설정 초기화
		m_curCanvasType	= CanvasType.CorrectedCanvas;
        m_mainCam		= Camera.main;

        m_isCurve       = false;

        //캔버스 크기 조정
        float height			        = Camera.main.orthographicSize * 2.0f;
        float width				        = height * Screen.width / Screen.height;
        m_screenSize		            = height < width ? height : width;
        m_inputCanvas.transform.localScale	        = new Vector3(m_screenSize, m_screenSize, 1f);
        m_correctedCanvas.transform.localScale = new Vector3(m_screenSize, m_screenSize, 1f);
	}


	/// <summary>
	/// 캔버스 전환
	/// </summary>
	/// <param name="switchTo">Switch to.</param>
	void SwitchCanvas(CanvasType switchTo)
	{
		if(switchTo == m_curCanvasType)				// * 캔버스 종류가 실제로 달라질 때만 실행
			return;

		m_curCanvasType	= switchTo;
		switch(switchTo)
		{
		case CanvasType.CorrectedCanvas:
			m_drawCanvas						= m_correctedCanvas;
			m_correctedCanvas.gameObject.layer	= c_layer_Canvas;
			m_inputCanvas.gameObject.layer		= c_layer_Culling;
			break;

		case CanvasType.InputCanvas:
			m_drawCanvas						= m_inputCanvas;
			m_correctedCanvas.gameObject.layer	= c_layer_Culling;
			m_inputCanvas.gameObject.layer		= c_layer_Canvas;

			m_inputCanvas.StartRender();
			m_correctedCanvas.CopyTo(m_inputCanvas);			// InputCanvas 로 전환할 때는 복사한다
			m_inputCanvas.EndRender();
			break;
		}
	}


	void Update () 
    {
		bool anyInputs;								// 현재 들어오는 인풋이 있는지

        // 9번이 캔버스 레이어, 10번이 컬링 레이어
        //int canvasLayer = 9;
        //int cullingLayer = 10;


#if UNITY_EDITOR
		anyInputs	= ProcessInputByMouse();		// 에디터 모드일 때는 마우스로
#else
		anyInputs	= ProcessInputByTouch();		// 아닐 때는 터치로
#endif
		if(!anyInputs && !m_applyFlag)				// * Input도 없는데 Apply도 안했을 경우, Apply를 해준다
		{
            if (m_inputPointList.Count != 0)
            {
                //apply 하기 전 correctedCanvas 로 교체
                //m_inputCanvas.Clear(Color.white);
				SwitchCanvas(CanvasType.InputCanvas);

				LinkedList<Vector2> correctedList = new LinkedList<Vector2>();

				LineDraw(m_inputPointList);
				SwitchCanvas(CanvasType.CorrectedCanvas);

				if(isCurve)
				{
	                LinkedList<HermitPoint> selectedList = new LinkedList<HermitPoint>();
	                ShapeCalculator.SelectHermitPointFromInput(m_inputPointList, selectedList);
                    DrawShape(selectedList);
				}
				else
				{
					LinkedList<Vector2> selectedList	= new LinkedList<Vector2>();
					ShapeCalculator.SelectStraightPointFromInput(m_inputPointList, selectedList);
                    ShapeCalculator.CorrectionFromSelected(correctedList, selectedList);
                    DrawShape(correctedList);
				}

                //LineDraw(m_inputPointList);

                //SwitchCanvas(CanvasType.CorrectedCanvas);
                //DrawShape(selectedList);

                m_inputPointList.Clear();
            }
			m_applyFlag	= true;
			//Debug.Log ("Apply!");
		}
		else if(anyInputs)							// * 다시 입력이 들어오기 시작하면 Apply flag를 해제해준다
		{
			m_applyFlag	= false;

            //입력이 들어오기 시작했으니 input canvas 로 교체

			SwitchCanvas(CanvasType.InputCanvas);
		}
	}

	/// <summary>
	/// 마우스 입력 처리
	/// </summary>
	bool ProcessInputByMouse()
	{
		bool anyInputs	= false;

		if (Input.GetMouseButton(0))
		{
			anyInputs				= true;		// 입력 플래그 올리기

			Vector3 worldPosition	= InputPointToWorldPoint(Input.mousePosition);
            BrushDraw(WorldToCoordinate(worldPosition));
            m_inputPointList.AddLast(WorldToCoordinate(worldPosition));
		}

		return anyInputs;					// 입력중인지 여부도 리턴
	}

	/// <summary>
	/// 터치 입력 처리
	/// </summary>
	bool ProcessInputByTouch()
	{
		bool anyInputs	= false;
		int count		= Input.touchCount;
		if(count != 0)						// 입력이 존재하는 경우
		{
			anyInputs	= true;				// 입력 플래그 올리기

			Touch touch				= Input.GetTouch(0);        // 멀티터치가 아닐 때 0 이후의 터치를 사용
            Vector3 worldPosition = InputPointToWorldPoint(touch.position);
            BrushDraw(WorldToCoordinate(worldPosition));
            m_inputPointList.AddLast(WorldToCoordinate(worldPosition));
		}

		return anyInputs;
	}

	/// <summary>
	/// 입력 좌표 변환
	/// </summary>
	/// <returns>The point to world point.</returns>
	/// <param name="point">Point.</param>
	Vector3 InputPointToWorldPoint(Vector3 point)
	{
        point.z = m_mainCam.transform.localPosition.y;
		return m_mainCam.ScreenToWorldPoint(point);
	}

	/// <summary>
	/// 브러시 입력 처리
	/// </summary>
	/// <param name="worldPosition">World position.</param>
    void BrushDraw(Vector2 point)
    {
        m_drawCanvas.StartRender();
        m_drawCanvas.DrawPoint(point);
        m_drawCanvas.EndRender();
    }

    /// <summary>
    /// 월드 좌표를 텍스쳐 좌표로 변환한다.
    /// </summary>
    /// <param name="worldPoint"></param>
    /// <returns>texture coordinate</returns>
    Vector2 WorldToCoordinate(Vector3 worldPoint)
    {
        Vector2 coordinate;
        // world Point 는 가운데가 0,0 이고 -screenSize/2 에서 screenSize/2 범위의 값
        // coordinate 는 왼쪽 아래가 0,0이고 오른쪽 위가 screenSize, screenSize
        if (m_screenSize < 0.01f)
        {
            m_screenSize = 1.0f;
        }
        coordinate.x = (0.5f + worldPoint.x / m_screenSize) * m_drawCanvas.width;
        coordinate.y = (0.5f - worldPoint.z / m_screenSize) * m_drawCanvas.height;
        //Debug.Log(coordinate.x + " " + coordinate.y);
        return coordinate;
    }

    /// <summary>
    /// 벡터 리스트를 받아 Array로 바꾸어 직선을 그린다.
    /// </summary>
    /// <param name="pointList"></param>
    void LineDraw(LinkedList<Vector2> pointList)
    {
        m_drawCanvas.StartRender();
        Vector2[] pointArray = new Vector2[pointList.Count];
        pointList.CopyTo(pointArray,0);
        m_drawCanvas.DrawLine(pointArray);
        m_drawCanvas.EndRender();
    }
    /// <summary>
    /// HermitPoint 리스트를 받아 도형을 그린다.
    /// </summary>
    /// <param name="pointList"></param>
    void DrawShape(LinkedList<HermitPoint> pointList)
    {
        int listCount = pointList.Count;
        HermitPoint[] pointArray = new HermitPoint[listCount];
        pointList.CopyTo(pointArray, 0);
        m_drawCanvas.StartRender();
        m_drawCanvas.DrawCurve(pointArray);
        m_drawCanvas.EndRender();
    }

    void DrawShape(LinkedList<Vector2> pointList)
    {
        int listCount = pointList.Count;
        Vector2[] pointArray = new Vector2[listCount];
        pointList.CopyTo(pointArray, 0);
        m_drawCanvas.StartRender();
        m_drawCanvas.DrawLine(pointArray);
        m_drawCanvas.EndRender();
    }
}
